package com.jeesuite.apigateway.filter;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.jeesuite.apigateway.helper.IpHelper;
import com.jeesuite.passport.PassportConstants;
import com.jeesuite.passport.helper.AuthSessionHelper;
import com.jeesuite.passport.model.LoginSession;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;


/**
 * 
 * @description <br>
 * @author <a href="mailto:vakinge@gmail.com">vakin</a>
 * @date 2017年6月12日
 */
public class AccessControllerFilter extends ZuulFilter implements EnvironmentAware{

	private static Logger log = LoggerFactory.getLogger(AccessControllerFilter.class);
	private static final String MSG_401_UNAUTHORIZED = "{\"code\": 401,\"msg\":\"401 Unauthorized\"}";
	private static String MSG_403_FORBIDDEN = "{\"code\": 403,\"msg\":\"403 Forbidden\"}";

	private Map<String, Pattern> anonUris = new HashMap<>();
	private Map<String, Pattern> innerOnlyUris = new HashMap<>();
	private Map<String, String[]> urlRalatePermissionPrefixMap = new HashMap<>();
	

	private String contextpth;
	
	@Override
	public String filterType() {
		return "pre";
	}

	@Override
	public int filterOrder() {
		return 1;
	}

	@Override
	public boolean shouldFilter() {
		return true;
	}

	@Override
	public Object run() {
		RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		
		// /api/tax/list -> tax 
		String appNamePath = request.getRequestURI().substring(contextpth.length() + 1);
		if(appNamePath.contains("/"))appNamePath = appNamePath.substring(0,appNamePath.indexOf("/"));
		
//		if(request.getRequestURI().contains("/api-docs")){
//			ctx.setSendZuulResponse(false);
//			ctx.setResponseStatusCode(401);
//			ctx.setResponseBody(MSG_401_UNAUTHORIZED);
//			return null;
//		}
		
		//是否仅内网可访问
		boolean innerOnly = innerOnlyUris.containsKey(appNamePath) && innerOnlyUris.get(appNamePath).matcher(request.getRequestURI()).matches();
		if(innerOnly){
			boolean innerIp = IpHelper.isInnerIp(IpHelper.getIpAddr(request));
			if(!innerIp){
				ctx.setSendZuulResponse(false);
				ctx.setResponseStatusCode(403);
				ctx.setResponseBody(MSG_403_FORBIDDEN);
				return null;
			}
		}
		
		//是否需要忽略登录检查
		boolean ignoreCheck = anonUris.containsKey(appNamePath) && anonUris.get(appNamePath).matcher(request.getRequestURI()).matches();
		
		LoginSession sesion = null;
		String[] permissionPrefixs = null;

		String sessionId = getSessionId(request);
		if(ignoreCheck == false && sessionId == null) {
			ctx.setSendZuulResponse(false);
			ctx.setResponseStatusCode(401);
			ctx.setResponseBody("{\"code\": 411,\"msg\":\"[accessToken] is required\"}");
			return null;
		}
		
		sesion = AuthSessionHelper.getLoginSession(sessionId);
		if(ignoreCheck == false && sesion == null){
			ctx.setSendZuulResponse(false);
			ctx.setResponseStatusCode(401);
			ctx.setResponseBody(MSG_401_UNAUTHORIZED);
			return null;
		}
		
		if(sesion != null){			
			if(sesion.getPermissions() != null && !sesion.getPermissions().isEmpty()){	
				permissionPrefixs = urlRalatePermissionPrefixMap.get(appNamePath);
			}
			ctx.addZuulRequestHeader(PassportConstants.HEADER_AUTH_USER,sesion.toEncodeString(permissionPrefixs));
			//如果即将过期刷新过期时间
			AuthSessionHelper.refreshIfWouldExpire(sesion);
		}else{		
			//匿名用户token透传
			if(sessionId != null && sessionId.startsWith(LoginSession.ANON_TOKEN_PREFIX)){
				//TODO 匿名用户
			}else{//生成匿名用户accesstoken				
				String anonymousToken = LoginSession.getAnonymousToken();
				Cookie cookie = new Cookie(PassportConstants.ACCESSTOKEN, anonymousToken);
				cookie.setPath("/");
				cookie.setMaxAge(3600);
				ctx.getResponse().addCookie(cookie);
			}
		}

		return null;
	}
	
	
	public static String getSessionId(HttpServletRequest request) {
		Cookie[] cookies = request.getCookies();
		if(cookies == null)return null;
		for (Cookie cookie : cookies) {
			if(PassportConstants.AYG_SESSION_NAME.equals(cookie.getName())){
				return cookie.getValue();
			}
		}
		return null;
	}
	

	@Override
	public void setEnvironment(Environment environment) {
		contextpth = environment.getProperty("server.context-path");
		if(StringUtils.isNotBlank(contextpth) && contextpth.endsWith("/")){
			contextpth = contextpth.substring(0, contextpth.length() - 1);
		}
		RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment, "zuul.routes.");
		Map<String, Object> subProperties = resolver.getSubProperties("");
        for (String key : subProperties.keySet()) {
        	String pathKey = key.split("\\.")[0] + ".path";
        	String basePath = contextpth + resolver.getProperty(pathKey).replaceAll("/\\*+", "");// /api/tax
        	String appPathName = resolver.getProperty(pathKey).replaceAll("/|\\*+", "");   // tax
        	//
        	boolean isAnonymous;
        	if((isAnonymous = key.contains("anonymous.uri")) || key.contains("inneronly.uri")){
        		String regex = basePath + subProperties.get(key).toString().replaceAll(";", "|"+ basePath).replaceAll("\\*+", ".*");
    			Pattern pattern = Pattern.compile(regex);
    			if(isAnonymous){
    				anonUris.put(appPathName, pattern);
    			}else{
    				innerOnlyUris.put(appPathName, pattern);
    			}
        	}else if(key.contains("permission.prefix")){
        		if(subProperties.get(key) != null && StringUtils.isNotBlank(subProperties.get(key).toString())){        			
        			String[] prefixs = subProperties.get(key).toString().split(";|,");
        			urlRalatePermissionPrefixMap.put(appPathName, prefixs);
        		}
        	}
        }
	}
	
	public static void main(String[] args) throws JsonProcessingException, IOException {
		
	}

}
